#include <jni.h>
#include <string>
#include <android/log.h>
#include <android/bitmap.h>
#include <android/native_window.h>
#include <android/native_window_jni.h>

#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,__VA_ARGS__ ,__VA_ARGS__) // 定义LOGE类型

/**
 * rgb转nv21
 * @param rgb
 * @param nv21
 * @param width
 * @param height
 * @param opposite
 */
void RGB_TO_NV21(unsigned int *rgb, unsigned char *nv21, unsigned int width, unsigned int height,
                 bool opposite) {
    int frameSize = width * height;
    int yIndex = 0;
    int uvIndex = frameSize;
    int a, R, G, B, Y, U, V;
    int index = 0;
    for (int j = 0; j < height; j++) {
        for (int i = 0; i < width; i++) {
            if (opposite) {
                B = (rgb[index] & 0xff0000) >> 16;
                G = (rgb[index] & 0xff00) >> 8;
                R = (rgb[index] & 0xff) >> 0;
            } else {
                R = (rgb[index] & 0xff0000) >> 16;
                G = (rgb[index] & 0xff00) >> 8;
                B = (rgb[index] & 0xff) >> 0;
            }
            Y = ((66 * R + 129 * G + 25 * B + 128) >> 8) + 16;
            U = ((-38 * R - 74 * G + 112 * B + 128) >> 8) + 128;
            V = ((112 * R - 94 * G - 18 * B + 128) >> 8) + 128;

            nv21[yIndex++] = (char) ((Y < 0) ? 0 : ((Y > 255) ? 255 : Y));

            if (j % 2 == 0 && index % 2 == 0) {
                nv21[uvIndex++] = (char) ((V < 0) ? 0 : ((V > 255) ? 255 : V));
                nv21[uvIndex++] = (char) ((U < 0) ? 0 : ((U > 255) ? 255 : U));
            }
            index++;
        }
    }
}

/**
 * nv21转rgb
 * @param nv21
 * @param width
 * @param height
 * @param rgb
 * @param isRGB
 */
void NV21_TO_RGB(unsigned char *nv21, int width, int height, unsigned int *rgb, bool isReversion) {
    const int frameSize = width * height;
    const int ii = 0;
    const int ij = 0;
    const int di = +1;
    const int dj = +1;

    int a = 0;
    for (int i = 0, ci = ii; i < height; ++i, ci += di) {
        for (int j = 0, cj = ij; j < width; ++j, cj += dj) {
            int y = (0xff & ((unsigned char) nv21[ci * width + cj]));
            int v = (0xff & ((unsigned char) nv21[frameSize + (ci >> 1) * width + (cj & ~1) + 0]));
            int u = (0xff & ((unsigned char) nv21[frameSize + (ci >> 1) * width + (cj & ~1) + 1]));
            y = y < 16 ? 16 : y;
            int r, g, b;
            if (isReversion) {
                b = (int) (1.164f * (y - 16) + 1.596f * (v - 128));
                g = (int) (1.164f * (y - 16) - 0.813f * (v - 128) - 0.391f * (u - 128));
                r = (int) (1.164f * (y - 16) + 2.018f * (u - 128));
            } else {
                r = (int) (1.164f * (y - 16) + 1.596f * (v - 128));
                g = (int) (1.164f * (y - 16) - 0.813f * (v - 128) - 0.391f * (u - 128));
                b = (int) (1.164f * (y - 16) + 2.018f * (u - 128));
            }

            r = r < 0 ? 0 : (r > 255 ? 255 : r);
            g = g < 0 ? 0 : (g > 255 ? 255 : g);
            b = b < 0 ? 0 : (b > 255 ? 255 : b);

            rgb[a++] = 0xff000000 | (r << 16) | (g << 8) | b;
        }
    }
}

/**
 * NV21裁切
 * @param srcNV21
 * @param srcWidth
 * @param srcHeight
 * @param outNV21
 * @param outLeft
 * @param outTop
 * @param outWidth
 * @param outHeight
 */
void clipNv21ToNv21(unsigned char *srcNV21, unsigned int srcWidth, unsigned int srcHeight,
                    unsigned char *outNV21, unsigned int outLeft, unsigned int outTop,
                    unsigned int outWidth,
                    const int outHeight) {
    unsigned char *newSrcNV21 = srcNV21;
    int i = 0;
    //关键二之一。
    //取整。估计不同平台要求取整范围有差异。
    //这样计算的结果，有可能差一个像素。宽高最好对应调整。
    int clipLeft = (int) (outLeft + 1) / 2 * 2;
    int clipTop = (int) (outTop + 1) / 2 * 2;
    //移动到指定位置
    newSrcNV21 += clipTop * srcWidth + clipLeft;
    //复制Y
    for (i = 0; i < outHeight; i++) {
        memcpy(outNV21, newSrcNV21, outWidth);
        newSrcNV21 += srcWidth;
        outNV21 += outWidth;
    }
    //复制U/V
    newSrcNV21 = srcNV21 + srcWidth * srcHeight;
    newSrcNV21 += (clipTop * srcWidth / 2 + clipLeft);
    //关键二之二：
    for (i = 0; i < outHeight / 2; i++) {
        memcpy(outNV21, newSrcNV21, outWidth);
        newSrcNV21 += srcWidth;
        outNV21 += outWidth;
    }
}

/**
 * 镜像NV21
 * @param srcNV21
 * @param srcWidth
 * @param srcHeight
 */
void MirrorNV21(unsigned char *srcNV21, unsigned int srcWidth, unsigned int srcHeight) {
    int i;
    int left, right;
    jbyte temp;
    int startPos = 0;

    // mirror Y
    for (i = 0; i < srcHeight; i++) {
        left = startPos;
        right = startPos + srcWidth - 1;
        while (left < right) {
            temp = srcNV21[left];
            srcNV21[left] = srcNV21[right];
            srcNV21[right] = temp;
            left++;
            right--;
        }
        startPos += srcWidth;
    }

    // mirror U and V
    int offset = srcWidth * srcHeight;
    startPos = 0;
    for (i = 0; i < srcHeight / 2; i++) {
        left = offset + startPos;
        right = offset + startPos + srcWidth - 2;
        while (left < right) {
            temp = srcNV21[left];
            srcNV21[left] = srcNV21[right];
            srcNV21[right] = temp;
            left++;
            right--;

            temp = srcNV21[left];
            srcNV21[left] = srcNV21[right];
            srcNV21[right] = temp;
            left++;
            right--;
        }
        startPos += srcWidth;
    }
}


/**
 * NV21旋转90度
 * @param srcNv21
 * @param width
 * @param height
 * @param outNV21
 */
void rotate90NV21(unsigned char *srcNv21, unsigned int width, unsigned int height,
                  unsigned char *outNV21) {
// 旋转 Y 亮度
    int i = 0;
    for (int x = 0; x < width; x++) {
        for (int y = height - 1; y >= 0; y--) {
            outNV21[i] = srcNv21[y * width + x];
            i++;
        }
    }
    // 旋转 U 和 V 颜色分量
    i = width * height * 3 / 2 - 1;
    for (int x = width - 1; x > 0; x = x - 2) {
        for (int y = 0; y < height / 2; y++) {
            outNV21[i] = srcNv21[(width * height) + (y * width) + x];
            i--;
            outNV21[i] = srcNv21[(width * height) + (y * width) + (x - 1)];
            i--;
        }
    }
}

/**
 * NV21旋转180度
 * @param srcNv21
 * @param width
 * @param height
 * @param outNV21
 */
void rotate180NV21(unsigned char *srcNv21, unsigned int width, unsigned int height,
                   unsigned char *outNV21) {
    // 旋转 Y 亮度
    int i = 0;
    int count = 0;

    for (i = width * height - 1; i >= 0; i--) {
        outNV21[count] = srcNv21[i];
        count++;
    }
    // 旋转 U 和 V 颜色分量
    i = width * height * 3 / 2 - 1;
    for (i = width * height * 3 / 2 - 1; i >= width
                                              * height; i -= 2) {
        outNV21[count++] = srcNv21[i - 1];
        outNV21[count++] = srcNv21[i];
    }
}

/**
 * NV21旋转270度
 * @param srcNv21
 * @param width
 * @param height
 * @param outNV21
 */
void rotate270NV21(unsigned char *srcNv21, unsigned int width, unsigned int height,
                   unsigned char *outNV21) {
    // 旋转 Y 亮度
    int i = 0;
    for (int x = width - 1; x >= 0; x--) {
        for (int y = 0; y < height; y++) {
            outNV21[i] = srcNv21[y * width + x];
            i++;
        }
    }
    // 旋转 U 和 V 颜色分量
    i = width * height;
    for (int x = width - 1; x > 0; x = x - 2) {
        for (int y = 0; y < height / 2; y++) {
            outNV21[i] = srcNv21[(width * height) + (y * width) + (x - 1)];
            i++;
            outNV21[i] = srcNv21[(width * height) + (y * width) + x];
            i++;
        }
    }
}

void
rotateNV21(unsigned char *srcNv21, unsigned int width, unsigned int height,
           unsigned char *outNV21,
           unsigned int angle) {
    switch (angle) {
        case 90:
            rotate90NV21(srcNv21, width, height, outNV21);
            break;
        case 180:
            rotate180NV21(srcNv21, width, height, outNV21);
            break;
        case 270:
            rotate270NV21(srcNv21, width, height, outNV21);
            break;
        default:
            LOGE("NV21角度旋转未找到可用角度(90,180,270)");
            break;
    }
}


extern "C"
JNIEXPORT void JNICALL
Java_com_lyn_image_1tools_ImageUtil_nativeBitmapToNV21(JNIEnv *env, jobject thiz,
                                                       jobject bitmap,
                                                       jbyteArray out_nv21) {
    // TODO: implement nativeBitmapToNV21()
    AndroidBitmapInfo bitmapInfo;
    if (AndroidBitmap_getInfo(env, bitmap, &bitmapInfo) < 0) {
        LOGE("AndroidBitmap_getInfo(env, bitmap, bitmapInfo) < 0");
        return;
    }
    void **addrPtr;
    env->NewIntArray(bitmapInfo.width * bitmapInfo.height);
    if (AndroidBitmap_lockPixels(env, bitmap, (void **) &addrPtr) < 0) {
        LOGE("AndroidBitmap_lockPixels(env, bitmap, (void **)rgba) < 0");
        return;
    }
    if (AndroidBitmap_unlockPixels(env, bitmap) < 0) {
        LOGE("AndroidBitmap_unlockPixels(env, bitmap) < 0");
        return;
    }
    jbyte *jByteOutNV21 = env->GetByteArrayElements(out_nv21, 0);
    RGB_TO_NV21((unsigned int *) addrPtr, (unsigned char *) jByteOutNV21, bitmapInfo.width,
                bitmapInfo.height,
                true);
    env->ReleaseByteArrayElements(out_nv21, jByteOutNV21, 0);
}
extern "C"
JNIEXPORT void JNICALL
Java_com_lyn_image_1tools_ImageUtil_nativeClipNv21ToNv21(JNIEnv *env, jobject thiz,
                                                         jbyteArray src_nv21, jint src_width,
                                                         jint src_height, jbyteArray out_nv21,
                                                         jint out_left, jint out_top,
                                                         jint out_with,
                                                         jint out_height) {
    // TODO: implement nativeClipNv21ToNv21()
    jbyte *jbSrcNv21 = env->GetByteArrayElements(src_nv21, 0);
    jbyte *jbOutNv21 = env->GetByteArrayElements(out_nv21, 0);
    clipNv21ToNv21((unsigned char *) jbSrcNv21, src_width, src_height,
                   (unsigned char *) jbOutNv21,
                   out_left,
                   out_top,
                   out_with, out_height);
    env->ReleaseByteArrayElements(src_nv21, jbSrcNv21, 0);
    env->ReleaseByteArrayElements(out_nv21, jbOutNv21, 0);
}

extern "C"
JNIEXPORT void JNICALL
Java_com_lyn_image_1tools_ImageUtil_nativeMirrorNV21(JNIEnv *env, jobject thiz,
                                                     jbyteArray src_nv21,
                                                     jint src_width, jint src_height) {
    // TODO: implement nativeMirrorNV21()
    jbyte *jbSrcNv21 = env->GetByteArrayElements(src_nv21, 0);
    MirrorNV21((unsigned char *) jbSrcNv21, src_width, src_height);
    env->ReleaseByteArrayElements(src_nv21, jbSrcNv21, 0);
}


extern "C"
JNIEXPORT void JNICALL
Java_com_lyn_image_1tools_ImageUtil_nativeRotateNV21(JNIEnv *env, jobject thiz,
                                                     jbyteArray src_nv21,
                                                     jint src_width, jint src_height,
                                                     jbyteArray out_nv21, jint angle) {
    // TODO: implement nativeRotateNV21()
    jbyte *jbSrcNv21 = env->GetByteArrayElements(src_nv21, 0);
    jbyte *jbOutNv21 = env->GetByteArrayElements(out_nv21, 0);
    rotateNV21((unsigned char *) jbSrcNv21, src_width, src_height, (unsigned char *) jbOutNv21,
               angle);
    env->ReleaseByteArrayElements(src_nv21, jbSrcNv21, 0);
    env->ReleaseByteArrayElements(out_nv21, jbOutNv21, 0);
}



extern "C"
JNIEXPORT void JNICALL
Java_com_lyn_image_1tools_ImageUtil_nativeNV21ToRGB(JNIEnv *env, jobject thiz, jbyteArray src_nv21,
                                                    jint src_width, jint src_height, jintArray rgb,
                                                    jboolean is_rgb) {
    // TODO: implement nativeNV21ToRGB()
    jbyte *jbSrcNv21 = env->GetByteArrayElements(src_nv21, 0);
    jint *jintRgb = env->GetIntArrayElements(rgb, 0);
    NV21_TO_RGB((unsigned char *) jbSrcNv21, src_width, src_height, (unsigned int *) jintRgb,
                is_rgb);
    env->ReleaseByteArrayElements(src_nv21, jbSrcNv21, 0);
    env->ReleaseIntArrayElements(rgb, jintRgb, 0);
}

static ANativeWindow *aNativeWindow;

extern "C"
JNIEXPORT void JNICALL
Java_com_lyn_image_1tools_ImageUtil_nativeSetSurface(JNIEnv *env, jobject thiz, jobject surface) {
    // TODO: implement nativeSetSurface()
    aNativeWindow = ANativeWindow_fromSurface(env, surface);
}

extern "C"
JNIEXPORT void JNICALL
Java_com_lyn_image_1tools_ImageUtil_nativeShowBGRToSurface(JNIEnv *env, jobject thiz, jintArray bgr,
                                                           jint width, jint height) {
    // TODO: implement nativeShowBGRToSurface()
    jint *jintRgb = env->GetIntArrayElements(bgr, 0);
    if (aNativeWindow == NULL) {
        LOGE("aNativeWindow==NULL!");
        return;
    }
    if (ANativeWindow_setBuffersGeometry(aNativeWindow, width, height,
                                         WINDOW_FORMAT_RGBA_8888) <
        0) {
        LOGE("设置数据格式错误!");
        return;
    }
    ANativeWindow_Buffer aNativeWindowBuffer;
    int lock = ANativeWindow_lock(aNativeWindow, &aNativeWindowBuffer, NULL);
    if (lock < 0) {
        LOGE("%s", "锁定缓冲区失败!"+lock);
        return;
    }
    unsigned int *dst = (unsigned int *) aNativeWindowBuffer.bits;
    memcpy(dst, (unsigned int *) jintRgb, width * height * 4);
    ANativeWindow_unlockAndPost(aNativeWindow);
    env->ReleaseIntArrayElements(bgr, jintRgb, 0);
}
